package com.dap.configuration.web.config.controller;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.baidu.disconf.core.common.constants.DisConfigTypeEnum;
import com.baidu.disconf.web.service.app.bo.App;
import com.baidu.disconf.web.service.app.service.AppMgr;
import com.baidu.disconf.web.service.config.bo.Config;
import com.baidu.disconf.web.service.config.form.ConfNewForm;
import com.baidu.disconf.web.service.config.form.ConfNewItemForm;
import com.baidu.disconf.web.service.config.service.ConfigMgr;
import com.baidu.disconf.web.service.config.vo.ConfListVo;
import com.baidu.disconf.web.service.env.bo.Env;
import com.baidu.disconf.web.service.env.service.EnvMgr;
import com.baidu.disconf.web.utils.CodeUtils;
import com.baidu.disconf.web.web.config.validator.ConfigValidator;
import com.baidu.disconf.web.web.config.validator.FileUploadValidator;
import com.baidu.dsp.common.constant.WebConstants;
import com.baidu.dsp.common.constraint.validation.PageOrderValidator;
import com.baidu.dsp.common.controller.BaseController;
import com.baidu.dsp.common.dao.Columns;
import com.baidu.dsp.common.exception.DocumentNotFoundException;
import com.baidu.dsp.common.exception.FieldException;
import com.baidu.dsp.common.exception.FileUploadException;
import com.baidu.dsp.common.vo.JsonObjectBase;
import com.baidu.ub.common.db.DaoPageResult;
import com.dap.configuration.service.config.bo.ConfigParam;
import com.dap.configuration.service.config.form.ConfListExtensionForm;
import com.dap.configuration.service.config.form.ConfNewExtensionForm;
import com.dap.configuration.service.config.service.ConfigExtensionMgr;
import com.dap.configuration.service.config.vo.ConfListExtensionVo;
import com.dap.configuration.utils.UnRarZipUtil;

/**
 * 专用于配置读取
 * @author dengxiang
 * @version 2016-01-10
 */
@Controller
@RequestMapping(WebConstants.API_PREFIX + "/web/configExtension")
public class ConfigExtensionController extends BaseController {

	private static final Logger LOG = LoggerFactory
			.getLogger(ConfigExtensionController.class);

	@Autowired
	private ConfigExtensionMgr configExtensionMgr;

	@Autowired
	private ConfigMgr configMgr;
	
	@Autowired
	private AppMgr appMgr;
	
	@Autowired
	private EnvMgr envMgr;

	@Autowired
	private ConfigValidator configValidator;

	@Autowired
	private FileUploadValidator fileUploadValidator;

	/**
	 * 获取列表,有分页的
	 *
	 * @param confListForm
	 *
	 * @return
	 * @throws ConfigException 
	 */
	@RequestMapping(value = "/list", method = RequestMethod.GET)
	@ResponseBody
	public JsonObjectBase getConfigList(
			@Valid ConfListExtensionForm confListForm) {
		// 设置排序方式
		confListForm.getPage().setOrderBy(Columns.NAME);
		confListForm.getPage().setOrder(PageOrderValidator.ASC);
		DaoPageResult<ConfListExtensionVo> configs = configExtensionMgr
				.getConfigList(confListForm, true, false);

		return buildListSuccess(configs);
	}

	/**
	 * 获取某个(查询信息包含bankKey、userId)
	 * @param configId
	 * @return
	 */
	@RequestMapping(value = "/{configId}", method = RequestMethod.GET)
	@ResponseBody
	public JsonObjectBase getConfig(@PathVariable long configId)
			{
			// 业务校验
			configValidator.valideConfigExist(configId);

			ConfListExtensionVo config = configExtensionMgr.getConfVo(configId);

			return buildSuccess(config);
	}

	/**
	 * 配置项的新建
	 *
	 * @param confNewForm
	 *
	 * @return
	 */
	@RequestMapping(value = "/item", method = RequestMethod.POST)
	@ResponseBody
	public JsonObjectBase newItem(ConfNewExtensionForm confNewForm)
			{
			// 业务校验
			configExtensionMgr.validateNew(confNewForm, DisConfigTypeEnum.ITEM);

			//新增
			Config config = configExtensionMgr.newConfig(confNewForm, DisConfigTypeEnum.ITEM);

			return buildSuccess("创建成功configId="+config.getId());
	}

	/**
	 * 配置文件的新建(使用上传配置文件)
	 *
	 * @param confNewForm
	 * @param file
	 *
	 * @return
	 */
	@ResponseBody
	@RequestMapping(value = "/file", method = RequestMethod.POST)
	public JsonObjectBase newFile(@Valid ConfNewForm confNewForm,
			@RequestParam("myfilerar") MultipartFile file, String bankKey,
			String userId){
			// 校验
			int fileSize = 1024 * 1024 * 4;
			String[] allowExtName = { ".properties", ".xml" };
			fileUploadValidator.validateFile(file, fileSize, allowExtName);

			// 更新
			String fileContent = "";
			try {
				fileContent = new String(file.getBytes(), "GB2312");
				LOG.info("receive file: " + fileContent);

			} catch (Exception e) {

				LOG.error(e.toString());
				throw new FileUploadException("upload file error", e);
			}

			// 创建配置文件表格
			ConfNewItemForm confNewItemForm = new ConfNewItemForm(confNewForm);
			String fileName = null ;
			try {
				fileName = URLDecoder.decode(file.getOriginalFilename(),"UTF-8");
			} catch (UnsupportedEncodingException e1) {
				throw new FieldException("config", "文件名称转码出错", null);
			}
			confNewItemForm.setKey(fileName);
			confNewItemForm.setValue(fileContent);

			ConfNewExtensionForm form = new ConfNewExtensionForm();
			form.setAppId(confNewItemForm.getAppId());
			form.setBankKey(bankKey);
			form.setEnvId(confNewItemForm.getEnvId());
			form.setKey(confNewItemForm.getKey());
			form.setUserId(userId);
			form.setValue(confNewItemForm.getValue());
			form.setVersion(confNewItemForm.getVersion());
			
			// 业务校验
			configExtensionMgr.validateNew(form, DisConfigTypeEnum.FILE);
			//
			Config config = configExtensionMgr.newConfig(form, DisConfigTypeEnum.FILE);
			return buildSuccess("创建成功configId="+config.getId());
	}

	/**
	 * 配置文件的新建(使用文本)
	 *
	 * @param confNewForm
	 * @param fileContent
	 * @param fileName
	 *
	 * @return
	 * @throws ConfigException 
	 */
	@ResponseBody
	@RequestMapping(value = "/filetext", method = RequestMethod.POST)
	public JsonObjectBase newFileWithText(@Valid ConfNewForm confNewForm,
			@NotNull String fileContent, @NotNull String fileName,
			@NotNull String bankKey, @NotNull String userId) {
			if(StringUtils.isEmpty(fileName)){
				throw new FieldException("config", "配置文件名称不能为空", null);
			}
			// 创建配置文件表格
			ConfNewItemForm confNewItemForm = new ConfNewItemForm(confNewForm);
			confNewItemForm.setKey(fileName);
			confNewItemForm.setValue(fileContent);
			ConfNewExtensionForm form = new ConfNewExtensionForm();
			form.setAppId(confNewItemForm.getAppId());
			form.setBankKey(bankKey);
			form.setEnvId(confNewItemForm.getEnvId());
			form.setKey(confNewItemForm.getKey());
			form.setUserId(userId);
			form.setValue(fileContent);
			form.setVersion(confNewForm.getVersion());
			// 业务校验
			configExtensionMgr.validateNew(form, DisConfigTypeEnum.FILE);
			//新建
			Config config = configExtensionMgr.newConfig(form, DisConfigTypeEnum.FILE);
			return buildSuccess("创建成功configId="+config.getId());
	}

	/**
	 * 批量下载配置文件(条件查找)
	 * @param confListForm
	 * @return
	 * @throws IOException 
	 * @throws ConfigException 
	 */
	@RequestMapping(value = "/downloadListByParam", method = RequestMethod.GET)
	public HttpEntity<byte[]> downloadListByParam(
			@Valid ConfListExtensionForm confListForm) throws IOException {
			// 数据据结果
			if(confListForm.getBankKey() == null || confListForm.getBankKey()==""){
				throw new FieldException("config", "bankKey不能为空", null);
			}
			// get files
			List<File> fileList = configExtensionMgr
					.getDisonfFileList(confListForm);
			App app = appMgr.getById(confListForm.getAppId());
			Env env = envMgr.getById(confListForm.getEnvId());
			// prefix
			String prefixString = app.getName() + "_" + env.getName()+ "_" 
					+ confListForm.getVersion() + "_" + confListForm.getBankKey();
			
			HttpHeaders header = new HttpHeaders();
			String targetFileString = "";
			File targetFile = null;
			byte[] res = null;
			try {
				targetFileString = UnRarZipUtil.tarFiles("tmp", prefixString,
						fileList);
				targetFile = new File(targetFileString);
				res = IOUtils.toByteArray(new FileInputStream(targetFile));
			} catch (Exception e) {
				throw new FieldException("config", "找不到文档", null);
			}
			header.set("Content-Disposition", "attachment; filename="
					+ targetFile.getName());
			header.setContentLength(res.length);
			return new HttpEntity<byte[]>(res, header);
			//file关闭
	}

	/**
	 * 批量上传（压缩文件）
	 * @param confNewForm
	 * @param file
	 * @return
	 * @throws Exception 
	 * @throws ConfigException 
	 */
	@ResponseBody
	@RequestMapping(value = "/batchFile", method = RequestMethod.POST)
	public JsonObjectBase uploadBatchFile(@Valid String userId,
			@RequestParam("myfilerar") MultipartFile file) throws Exception
			{
			//将MultipartFile压缩包转换为List<File>
			List<File> list  =  UnRarZipUtil.deCompress(file);
			//从list中找出param.properties 
			//解析找出的param.properties，获取内容字符串
			//通过&分隔符获取四个参数appId、envId、version、bankKey
			//如果找到的文件不是param.properties，则获取文件内容放入到contentList
			FileReader reader = null;
			BufferedReader br = null;
			StringBuffer content = new StringBuffer();//参数数据
			List<Config> configList = new ArrayList<Config>();//用于list
			File tmp = null;//临时文件
			for (int i = 0; i < list.size(); i++) {
				tmp = list.get(i);
				if (tmp.getName() == "param.properties"
						|| tmp.getName().equals("param.properties")) {
						String str = null;
						reader = new FileReader(tmp);
						br = new BufferedReader(reader);
						while ((str = br.readLine()) != null) {
							content.append(str);
						}
						list.remove(i);
						break;
				}
			}
			if(content.length()<=0){
				throw new FieldException("config", "上传批量配置文件内容错误，必须包含param.properties参数文件", null);
			}
			ConfigParam configParam = configExtensionMgr.convertParam(content.toString());
			if (configParam != null) {
					StringBuffer fileTemp = null;//临时数据
					for (int n = 0; n < list.size(); n++) {
						tmp = list.get(n);
						if (tmp.getName() == "config_item_param.properties"|| tmp.getName().equals("config_item_param.properties")) {
							reader = new FileReader(tmp);
							Properties properties = new Properties();//Properties文件工具类
							properties.load(reader);
							Set set = properties.stringPropertyNames();
							Iterator iterator = set.iterator();
							while (iterator.hasNext()) {
								String key = (String) iterator.next();//配置项key值
								String value = properties.getProperty(key);//配置项value值
								Config configTemp = new Config();
								configTemp.setName(key);//配置项在下载的时候没有加密，所以这里不需要解码
								configTemp.setValue(CodeUtils.utf8ToUnicode(value));
								configTemp.setAppId(Long.parseLong(configParam.getAppName()));
								configTemp.setEnvId(Long.parseLong(configParam.getEnvName()));
								configTemp.setVersion(configParam.getVersion());
								configTemp.setBankKey(configParam.getBankKey());
								configTemp.setType(1);
								configTemp.setUserId(userId);
								configList.add(configTemp);
							}
						} else {
							String str = null;
							Config configTemp = new Config();
							//对于配置项或者配置文件名称存在"/"的需要解码
							configTemp.setName(URLDecoder.decode(tmp.getName()));
							reader = new FileReader(tmp);
							br = new BufferedReader(reader);
							fileTemp = new StringBuffer();
							while ((str = br.readLine()) != null) {
								fileTemp.append(str);
								fileTemp.append("\r\n");
							}
							configTemp.setAppId(Long.parseLong(configParam.getAppName()));
							configTemp.setEnvId(Long.parseLong(configParam.getEnvName()));
							configTemp.setVersion(configParam.getVersion());
							configTemp.setBankKey(configParam.getBankKey());
							configTemp.setValue(CodeUtils.utf8ToUnicode(fileTemp.toString()));
							configTemp.setType(0);
							configTemp.setUserId(userId);
							configList.add(configTemp);
						}
					}
					//循环list 
					//已经存在的文件做更新，不存在的文件做新增
					// 业务校验
					for (int m = 0; m < configList.size(); m++) {
						//通过appId、envId、version、type、bankKey、key五个参数到数据库查找数据，如果找到有数据则做更新，否则做修改
						Config configPara = configList.get(m);
						Config valiConfig = configExtensionMgr.getListByConfig(configPara);
						if (valiConfig != null) {
							//更新
							configMgr.updateItemValue(valiConfig.getId(),configPara.getValue());
							configMgr.notifyZookeeper(valiConfig.getId());
							//增加历史记录
							configExtensionMgr.newHistory(valiConfig.getId(), configPara.getValue(), "update");
						} else {
							//新增
							ConfNewExtensionForm form = new ConfNewExtensionForm();
							form.setAppId(configPara.getAppId());
							form.setEnvId(configPara.getEnvId());
							form.setVersion(configPara.getVersion());
							form.setKey(configPara.getName());
							form.setValue(configPara.getValue());
							form.setBankKey(configPara.getBankKey());
							form.setUserId(configPara.getUserId());
							if (configPara.getType() == 0) {
								configExtensionMgr.newConfig(form,
										DisConfigTypeEnum.FILE);//文件新增
							} else {
								configExtensionMgr.newConfig(form,
										DisConfigTypeEnum.ITEM);//配置项新增
							}
						}
						
					}
				return buildSuccess("创建成功");
			} else {
				return buildSuccess("创建失败，参数丢失");
			}
	}

	/**
	 * 配置项的更新
	 * @param configId
	 * @param value
	 * @return
	 * @throws ConfigException 
	 */
	@RequestMapping(value = "/item/{configId}", method = RequestMethod.PUT)
	@ResponseBody
	public JsonObjectBase updateItem(@PathVariable long configId, String value)
			 {
			// 业务校验
			configValidator.validateUpdateItem(configId, value);
			LOG.info("start to update config: " + configId);
			// 更新, 并写入数据库
			String emailNotification = "";
			emailNotification = configMgr.updateItemValue(configId, value);
			configExtensionMgr.newHistory(configId, value, "update");
			// 通知ZK
			configExtensionMgr.notifyZookeeper(configId);
			return buildSuccess(emailNotification);
			//如果zk更新失败，向管控平台增加zk出错信息
	}

	/**
	 * 配置文件的更新
	 *
	 * @param configId
	 * @param file
	 *
	 * @return
	 * @throws ConfigException 
	 */
	@ResponseBody
	@RequestMapping(value = "/file/{configId}", method = RequestMethod.POST)
	public JsonObjectBase updateFile(@PathVariable long configId,
			@RequestParam("myfilerar") MultipartFile file) {
			// 校验
			int fileSize = 1024 * 1024 * 4;
			String[] allowExtName = { ".properties", ".xml" };
			fileUploadValidator.validateFile(file, fileSize, allowExtName);
			String fileName = null ;
			try {
				fileName = URLDecoder.decode(file.getOriginalFilename(),"UTF-8");
			} catch (UnsupportedEncodingException e1) {
				throw new FieldException("config", "解密文件名出错", null);
			}
			// 业务校验
			configValidator.validateUpdateFile(configId,
					fileName);
			// 更新
			String emailNotification = "";
			try {
				String str = new String(file.getBytes(), "UTF-8");
				LOG.info("receive file: " + str);
				emailNotification = configMgr.updateItemValue(configId, str);
				configExtensionMgr.newHistory(configId, str, "update");
				LOG.info("update " + configId + " ok");
			} catch (Exception e) {
				LOG.error(e.toString());
				throw new FileUploadException("upload file error", e);
			}
			// 通知ZK
			configExtensionMgr.notifyZookeeper(configId);
			return buildSuccess(emailNotification);
	}

	/**
	 * 配置文件的更新(文本修改)
	 * @param configId
	 * @param fileContent
	 * @return
	 * @throws ConfigException 
	 */
	@ResponseBody
	@RequestMapping(value = "/filetext/{configId}", method = RequestMethod.PUT)
	public JsonObjectBase updateFileWithText(@PathVariable long configId,
			@NotNull String fileContent) {
			// 更新
			String emailNotification = "";
			// 业务校验
			configValidator.validateUpdateItem(configId, fileContent);
			LOG.info("receive file: " + fileContent);
			emailNotification = configExtensionMgr.updateItemValue(configId, fileContent);
			configExtensionMgr.newHistory(configId, fileContent, "update");
			LOG.info("update " + configId + " ok");
			// 通知ZK
			configExtensionMgr.notifyZookeeper(configId);
			return buildSuccess(emailNotification);
	}
	
	/**
     * delete
     *
     * @return
     */
    @RequestMapping(value = "/{configId}", method = RequestMethod.DELETE)
    @ResponseBody
    public JsonObjectBase delete(@PathVariable long configId) {

        configValidator.validateDelete(configId);

        configMgr.delete(configId);
        
        configExtensionMgr.newHistory(configId, null, "delete");

        return buildSuccess("删除成功");
    }
    
    /**
     * 下载
     *
     * @param configId
     *
     * @return
     */
    @RequestMapping(value = "/download/{configId}", method = RequestMethod.GET)
    public HttpEntity<byte[]> downloadDspBill(@PathVariable long configId) {

        // 业务校验
        configValidator.valideConfigExist(configId);

        ConfListVo config = configExtensionMgr.getConfVo(configId);

        HttpHeaders header = new HttpHeaders();
        String value = "";
        if(!StringUtils.isEmpty(config.getValue())){
        	value = config.getValue();
        }
        byte[] res = value.getBytes();
        if (res == null) {
            throw new DocumentNotFoundException(config.getKey());
        }

        String name = null;

        try {
            name = URLEncoder.encode(config.getKey(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
        	throw new FieldException("config", "配置名称转换异常", null);
        }

        header.set("Content-Disposition", "attachment; filename=" + name);
        header.setContentLength(res.length);
        return new HttpEntity<byte[]>(res, header);
    }

}
